package gin_frame

import (
	"context"
	"fmt"
	"github.com/hibiken/asynq"
	"github.com/hibiken/asynqmon"
	"go.uber.org/zap"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"gitee.com/yijimu/gin-frame/middlewares"
	"gitee.com/yijimu/gin-frame/utils"

	"gitee.com/yijimu/gin-frame/core"

	gs "github.com/swaggo/gin-swagger"
	"github.com/swaggo/gin-swagger/swaggerFiles"

	"github.com/gin-gonic/gin"

	"github.com/spf13/viper"
)

type ServerConfig struct {
	Path       string //配置类型：file->本地配置文件;consul->consul配置中心
	Type       string //配置路径: 如./config/config_develop.yaml 或 http://192.168.3.91:8500
	Token      string //consul的token，type=file时为空
	KvPath     string //consul的远程key/value路径
	RemoteType string //consul远程配置类型，如：yaml、json、hcl

	Validators map[string]core.Validators //gin 数据验证

	httpServer *http.Server //http服务

	router *gin.Engine //gin路由
}

// Init 初始化配置,包括：初始化配置、zap日志等等初始化行为
func (t *ServerConfig) Init() *ServerConfig {
	//配置初始化
	core.InitConfig(t.Path, t.Type, t.Token, t.KvPath, t.RemoteType) //初始化配置

	core.InitZap()
	Log = zap.L().With(
		zap.String("log_type", "log"),
	).WithOptions(
		zap.AddCaller(), //显示调用行
	)
	return t
}

// Gorm 初始化gorm
func (t *ServerConfig) Gorm() *ServerConfig {
	DB, DBList = core.InitGorm()
	return t
}

// Redis 初始化redis
func (t *ServerConfig) Redis() *ServerConfig {
	Redis, _ = utils.InitRedisClient(viper.GetString("redis.addr"), viper.GetString("redis.password"), viper.GetInt("redis.db"))
	return t
}

// RedisSync 初始化redis分布式锁
func (t *ServerConfig) RedisSync() *ServerConfig {
	RedisSync, _ = utils.InitRedsync(viper.GetString("redis.addr"), viper.GetString("redis.password"), viper.GetInt("redis.db"))
	return t
}

// InitHttp 初始化http服务
func (t *ServerConfig) InitHttp(initRouter func(router *gin.Engine)) *ServerConfig {
	t.Init() //初始化配置,包括：初始化配置、zap日志等等初始化行为

	//gin 验证器 中文翻译
	if err := core.InitTrans("zh", t.Validators); err != nil {
		fmt.Printf("init trans failed, err:%v\n", err)
	}

	//gin的mode设置:支持 release 和 debug 两种模式
	gin.SetMode(viper.GetString("app.env"))

	gin.ForceConsoleColor()

	t.router = gin.New()

	// ping 可以作为 心跳检测
	t.router.GET("/ping", func(c *gin.Context) {
		c.JSON(http.StatusOK, "pong")
	})

	//本地上传  根据 配置文件 来配置 静态资源 的 路由及路径
	if viper.GetInt("uploadImage.uploadType") == 1 {
		//创建图片上传的文件夹
		fmt.Println(viper.GetString("uploadImage.localUpload.path"))
		if err := utils.CreateDir(viper.GetString("uploadImage.localUpload.savePath")); err != nil {
			zap.L().Error(err.Error())
		}
		// 图片上传的静态路由
		t.router.Static(viper.GetString("uploadImage.localUpload.router"), viper.GetString("uploadImage.localUpload.savePath"))
	}
	t.router.Use(middlewares.GinLogger(), middlewares.GinRecovery(false), middlewares.Cors())

	initRouter(t.router) //初始化路由

	// 通过 RegisterRouter() 方法注册进来
	for _, router := range RouterHandleList {
		router(t.router)
	}

	return t
}

// RunHttp 开启并运行http服务
func (t *ServerConfig) RunHttp() {
	//可以添加一些公共部分的路由
	t.router.GET("/swagger/*any", gs.WrapHandler(swaggerFiles.Handler))
	//第一步、安装swag： go get -u github.com/swaggo/swag/cmd/swag
	//第二步、在项目根目录下使用swag工具生成接口文档数据，多出docs文件夹：swag init
	//第三步、swagger生成文档，访问http://127.0.0.1:8887/swagger/index.html

	//添加 asynq可视化ui界面 路由，可能在windows中无法使用，可以根据官网文档另选部署的方式
	asynqUI := asynqmon.New(asynqmon.Options{
		RootPath: "/asynq_ui",
		RedisConnOpt: asynq.RedisClientOpt{
			Addr:     viper.GetString("task.redis.addr"),
			Password: viper.GetString("task.redis.password"),
			DB:       viper.GetInt("task.redis.db"),
		},
	})
	t.router.Any(asynqUI.RootPath()+"/*a", gin.WrapH(asynqUI))

	// 特殊异常处理
	t.router.NoMethod(func(c *gin.Context) {
		c.JSON(http.StatusMethodNotAllowed, FailWithMessage("请求方法不允许"))
		return
	})
	t.router.NoRoute(func(c *gin.Context) {
		c.JSON(http.StatusNotFound, FailWithMessage("请求接口不存在"))
		return
	})

	errChan := make(chan error) //如果程序退出让服务注销掉

	t.httpServer = &http.Server{
		Addr:    ":" + viper.GetString("app.port"),
		Handler: t.router, // 初始化路由，加载APP目录中的路由配置
	}

	go (func() {
		fmt.Println("Listening and serving HTTP on :" + viper.GetString("app.port"))
		if err := t.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			errChan <- err
		}
	})()

	go (func() {
		sigC := make(chan os.Signal)
		signal.Notify(sigC, syscall.SIGINT, syscall.SIGTERM) //监听kill  或 ctrl+c 等退出程序
		errChan <- fmt.Errorf("%s", <-sigC)
	})()

	getErr := <-errChan //如果没有错误，则一直阻塞，不会进行下面的操作
	log.Println(getErr)

	//实现优雅关闭服务
	t.stopHttp()
}

// stopHttp 实现优雅关闭服务
func (t *ServerConfig) stopHttp() {
	log.Println("准备关闭服务...")
	// 创建一个5秒超时的context
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	if err := t.httpServer.Shutdown(ctx); err != nil {
		log.Fatal("关闭服务错误: ", err)
	}
	log.Println("服务已关闭！")
}

// RegisterRouter 注册路由
func RegisterRouter(groupName string, routers ...func(*gin.RouterGroup)) {
	if RouterHandleList == nil {
		RouterHandleList = []func(r *gin.Engine){}
	}

	RouterHandleList = append(RouterHandleList, func(r *gin.Engine) {
		rg := r.Group(groupName)
		for _, router := range routers {
			router(rg)
		}
	})
}
